﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;
using HeapSpecSupport;
using SimpleIR;

namespace Evaluator.EvalState
{
	/// <summary>
	/// Abstract class representing all the types of local variable storage type locations we can have:
	/// (1) Pointers
	/// (2) References
	/// (3) Primitive values (integers, floating point).
	/// (4) Structs
	/// </summary>
	internal sealed class EHomeLocation : ELocation
	{
		internal readonly bool IsGlobalLocation;

		private EValue m_val;
		private readonly BAPLayoutMap m_sval;

		private UseModRecord m_usemod;

		private EHomeLocation(IRType loctype, int address, bool isglobal, EValue mval, BAPLayoutMap sval, ulong time, long line)
			: base(address, loctype)
		{
			this.IsGlobalLocation = isglobal;
			this.m_val = mval;
			this.m_sval = sval;
			this.m_usemod = UseModRecord.MakeInitialUseMod(time, line);
		}

		internal EHomeLocation(IRType loctype, int address, bool isglobal, EMemoryValue mval, ulong time, long line)
			: this(loctype, address, isglobal, mval, null, time, line)
		{ ;}

		internal EHomeLocation(IRType loctype, int address, bool isglobal, EReference rval, ulong time, long line)
			: this(loctype, address, isglobal, rval, null, time, line)
		{ ;}

		internal EHomeLocation(IRType loctype, int address, bool isglobal, BAPLayoutMap sval, ulong time, long line)
			: this(loctype, address, isglobal, null, sval, time, line)
		{ ;}

		public override string ToString()
		{
			if(this.m_loctype is IRStructType)
				return this.m_sval.ToString();
			else
				return this.m_val.ToString() + ": " + this.m_usemod.ToString();
		}

		internal override EValue DoSimpleValueReadFromLocation(int arrayidx, IRBasicAccessPath bap, ulong time, long line)
		{
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			if(this.m_loctype is IRStructType)
			{
				Contract.Assert(bap != IRBasicAccessPath.EmptyAccessPath);

				return this.m_sval.ReadFieldValue(BAPLayoutEntry.INVALID_ARRAY_INDEX, bap, time, line);
			}
			else
			{
				Contract.Assert(bap == IRBasicAccessPath.EmptyAccessPath);

				this.m_usemod.DoRead(time, line);
				return this.m_val;
			}
		}

		internal override void DoSimpleValueWriteToLocation(int arrayidx, IRBasicAccessPath bap, EValue mval, ulong time, long line)
		{
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			if(this.m_loctype is IRStructType)
			{
				Contract.Assert(bap != IRBasicAccessPath.EmptyAccessPath);

				this.m_sval.WriteFieldValue(BAPLayoutEntry.INVALID_ARRAY_INDEX, bap, (EMemoryValue)mval, time, line);
			}
			else
			{
				Contract.Assert(bap == IRBasicAccessPath.EmptyAccessPath);
				Contract.Assert(((this.LocationType is IREnumType) && (((IREnumType)this.LocationType).UnerlyingNumericType == mval.TypeOfValue)) | (this.LocationType == mval.TypeOfValue), "Types do not match -- must be correctly cast to exact match on store.");

				this.m_usemod.DoWrite(time, line);
				this.m_val = mval;
			}
		}

		internal override void DoCompoundReadFromLocation(int arrayidx, IRBasicAccessPath bap, BAPLayoutMap into, ulong time, long line)
		{
			Contract.Assert(this.LocationType is IRStructType);
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			this.m_sval.ReadFieldValues(BAPLayoutEntry.INVALID_ARRAY_INDEX, bap, into, time, line);
		}

		internal override void DoCompoundWriteToLocation(int arrayidx, IRBasicAccessPath bap, BAPLayoutMap from, ulong time, long line)
		{
			Contract.Assert(this.LocationType is IRStructType);
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			this.m_sval.WriteFieldValues(BAPLayoutEntry.INVALID_ARRAY_INDEX, from, bap, time, line);
		}
	}
}

